#include "gb28181_ptz.h"
#include "../gb28181_service.h"
#include "../gb28181_utils.h"
#include "utils_log.h"
#include <sstream>
#include <regex>
#include <iomanip>
#include <algorithm>
#include <cctype>

namespace El {
namespace GB28181 {

void GB28181PTZ::ProcessDeviceControl(eXosip_event_t *event, eXosip_t *exosip_context,
                                    const SipServerConfig &config,
                                    PTZControlCallback controlCallback)
{
    if (!event || !event->request || !exosip_context) {
        EL_ERROR("Invalid parameters in ProcessDeviceControl");
        return;
    }

    osip_message_t *request = event->request;
    EL_INFO("Processing DeviceControl request for device {}", config.device_id);

    // Extract XML content
    std::string xml_content;
    osip_body_t *body = nullptr;
    osip_message_get_body(request, 0, &body);
    if (body && body->body) {
        xml_content = std::string(body->body);
    }

    if (xml_content.empty()) {
        EL_ERROR("No XML content found in DeviceControl request");
        SendDeviceControlResponse(exosip_context, event, false, "No XML content");
        return;
    }

    // Parse PTZ control info
    PTZControlInfo ptz_info;
    if (!ParsePTZControlXML(xml_content, ptz_info)) {
        EL_ERROR("Failed to parse PTZ control XML");
        SendDeviceControlResponse(exosip_context, event, false, "Invalid PTZ command");
        return;
    }

    EL_INFO("PTZ Control: device={}, channel={}, command={}, speed={}, preset={}",
             ptz_info.device_id, ptz_info.channel_id, 
             PTZCommandToString(ptz_info.command), ptz_info.speed, ptz_info.preset_id);

    // Validate parameters
    if (!ValidatePTZParams(ptz_info)) {
        EL_ERROR("Invalid PTZ parameters");
        SendDeviceControlResponse(exosip_context, event, false, "Invalid parameters");
        return;
    }

    // Execute PTZ control
    bool success = true;
    std::string error_msg;
    
    if (controlCallback) {
        try {
            success = controlCallback(ptz_info);
            if (!success) {
                error_msg = "PTZ control execution failed";
            }
        } catch (const std::exception& e) {
            success = false;
            error_msg = e.what();
            EL_ERROR("Exception in PTZ control callback: {}", e.what());
        }
    } else {
        EL_WARN("No PTZ control callback registered");
        success = false;
        error_msg = "PTZ control not supported";
    }

    // Send response
    SendDeviceControlResponse(exosip_context, event, success, error_msg);
}

void GB28181PTZ::SendDeviceControlResponse(eXosip_t *exosip_context, eXosip_event_t *event,
                                         bool success, const std::string &error_msg)
{
    if (!exosip_context || !event) {
        EL_ERROR("Invalid parameters in SendDeviceControlResponse");
        return;
    }

    int status_code = success ? 200 : 400;
    osip_message_t *response = nullptr;
    eXosip_message_build_answer(exosip_context, event->tid, status_code, &response);

    if (response) {
        if (!success && !error_msg.empty()) {
            // Add error message to response body
            std::string sn = GB28181Utils::GenerateSN();
            std::string response_xml = CreatePTZControlResponseXML("", sn, success, error_msg);
            osip_message_set_content_type(response, "Application/MANSCDP+xml");
            osip_message_set_body(response, response_xml.c_str(), response_xml.length());
        }
        
        eXosip_message_send_answer(exosip_context, event->tid, status_code, response);
        EL_INFO("Sent DeviceControl response: status={}, success={}", status_code, success);
    } else {
        EL_ERROR("Failed to build DeviceControl response");
    }
}

bool GB28181PTZ::ParsePTZControlXML(const std::string &xml, PTZControlInfo &info)
{
    // Extract basic fields
    info.device_id = ExtractXMLValue(xml, "DeviceID");
    
    // Extract PTZ command from PTZCmd or ControlType
    std::string ptz_cmd = ExtractXMLValue(xml, "PTZCmd");
    if (ptz_cmd.empty()) {
        // Try alternative format
        std::string control_type = ExtractXMLValue(xml, "ControlType");
        if (control_type == "ptz") {
            ptz_cmd = ExtractXMLValue(xml, "PTZCommand");
        }
    }
    
    if (ptz_cmd.empty()) {
        EL_ERROR("No PTZ command found in XML");
        return false;
    }
    
    // Parse PTZ command
    info.command = StringToPTZCommand(ptz_cmd);
    if (info.command == PTZCommand::UNKNOWN) {
        EL_ERROR("Unknown PTZ command: {}", ptz_cmd);
        return false;
    }
    
    // Parse speed
    info.speed = ParsePTZSpeed(xml);
    if (info.speed == 0) {
        info.speed = 64; // Default speed
    }
    
    // Parse preset ID for preset operations
    if (info.command == PTZCommand::PRESET_SET || 
        info.command == PTZCommand::PRESET_CALL || 
        info.command == PTZCommand::PRESET_DELETE) {
        info.preset_id = ParsePresetId(xml);
    }
    
    // Set channel ID (same as device ID for most cases)
    if (info.channel_id.empty()) {
        info.channel_id = info.device_id;
    }
    
    return true;
}

std::string GB28181PTZ::CreatePTZControlResponseXML(const std::string &device_id, 
                                                   const std::string &sn, 
                                                   bool success,
                                                   const std::string &error_msg)
{
    std::ostringstream xml;
    xml << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
        << "<Response>\n"
        << "<CmdType>DeviceControl</CmdType>\n"
        << "<SN>" << sn << "</SN>\n"
        << "<DeviceID>" << device_id << "</DeviceID>\n"
        << "<Result>" << (success ? "OK" : "ERROR") << "</Result>\n";
    
    if (!success && !error_msg.empty()) {
        xml << "<ErrorDescription>" << error_msg << "</ErrorDescription>\n";
    }
    
    xml << "</Response>\n";
    
    return xml.str();
}

std::string GB28181PTZ::PTZCommandToString(PTZCommand cmd)
{
    switch (cmd) {
        case PTZCommand::UP: return "UP";
        case PTZCommand::DOWN: return "DOWN";
        case PTZCommand::LEFT: return "LEFT";
        case PTZCommand::RIGHT: return "RIGHT";
        case PTZCommand::UP_LEFT: return "UPLEFT";
        case PTZCommand::UP_RIGHT: return "UPRIGHT";
        case PTZCommand::DOWN_LEFT: return "DOWNLEFT";
        case PTZCommand::DOWN_RIGHT: return "DOWNRIGHT";
        case PTZCommand::ZOOM_IN: return "ZOOMIN";
        case PTZCommand::ZOOM_OUT: return "ZOOMOUT";
        case PTZCommand::STOP: return "STOP";
        case PTZCommand::PRESET_SET: return "PRESET_SET";
        case PTZCommand::PRESET_CALL: return "PRESET_CALL";
        case PTZCommand::PRESET_DELETE: return "PRESET_DELETE";
        case PTZCommand::FOCUS_NEAR: return "FOCUS_NEAR";
        case PTZCommand::FOCUS_FAR: return "FOCUS_FAR";
        case PTZCommand::IRIS_OPEN: return "IRIS_OPEN";
        case PTZCommand::IRIS_CLOSE: return "IRIS_CLOSE";
        default: return "UNKNOWN";
    }
}

PTZCommand GB28181PTZ::StringToPTZCommand(const std::string &cmd_str)
{
    // Convert to uppercase for comparison
    std::string upper_cmd = cmd_str;
    std::transform(upper_cmd.begin(), upper_cmd.end(), upper_cmd.begin(), ::toupper);
    
    if (upper_cmd == "UP") return PTZCommand::UP;
    if (upper_cmd == "DOWN") return PTZCommand::DOWN;
    if (upper_cmd == "LEFT") return PTZCommand::LEFT;
    if (upper_cmd == "RIGHT") return PTZCommand::RIGHT;
    if (upper_cmd == "UPLEFT") return PTZCommand::UP_LEFT;
    if (upper_cmd == "UPRIGHT") return PTZCommand::UP_RIGHT;
    if (upper_cmd == "DOWNLEFT") return PTZCommand::DOWN_LEFT;
    if (upper_cmd == "DOWNRIGHT") return PTZCommand::DOWN_RIGHT;
    if (upper_cmd == "ZOOMIN") return PTZCommand::ZOOM_IN;
    if (upper_cmd == "ZOOMOUT") return PTZCommand::ZOOM_OUT;
    if (upper_cmd == "STOP") return PTZCommand::STOP;
    if (upper_cmd == "PRESET_SET") return PTZCommand::PRESET_SET;
    if (upper_cmd == "PRESET_CALL") return PTZCommand::PRESET_CALL;
    if (upper_cmd == "PRESET_DELETE") return PTZCommand::PRESET_DELETE;
    if (upper_cmd == "FOCUS_NEAR") return PTZCommand::FOCUS_NEAR;
    if (upper_cmd == "FOCUS_FAR") return PTZCommand::FOCUS_FAR;
    if (upper_cmd == "IRIS_OPEN") return PTZCommand::IRIS_OPEN;
    if (upper_cmd == "IRIS_CLOSE") return PTZCommand::IRIS_CLOSE;
    
    // Try to parse hex PTZ command format (legacy format)
    try {
        unsigned long hex_value = std::stoul(cmd_str, nullptr, 16);
        // Convert hex PTZ command to enum (simplified mapping)
        if (hex_value == 0x01) return PTZCommand::UP;
        if (hex_value == 0x02) return PTZCommand::DOWN;
        if (hex_value == 0x03) return PTZCommand::LEFT;
        if (hex_value == 0x04) return PTZCommand::RIGHT;
        if (hex_value == 0x00) return PTZCommand::STOP;
    } catch (const std::exception&) {
        // Not a hex value, continue
    }
    
    return PTZCommand::UNKNOWN;
}

std::string GB28181PTZ::GeneratePTZCmdString(PTZCommand cmd, int speed, int preset_id)
{
    std::ostringstream oss;
    
    // Generate 8-byte PTZ command according to GB/T 28181 standard
    uint8_t bytes[8] = {0xA5, 0x0F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    // Set speed (bytes 4 and 5)
    uint8_t normalized_speed = static_cast<uint8_t>(std::max(1, std::min(255, speed)));
    
    switch (cmd) {
        case PTZCommand::UP:
            bytes[3] = 0x08;
            bytes[4] = normalized_speed;
            break;
        case PTZCommand::DOWN:
            bytes[3] = 0x04;
            bytes[4] = normalized_speed;
            break;
        case PTZCommand::LEFT:
            bytes[3] = 0x02;
            bytes[5] = normalized_speed;
            break;
        case PTZCommand::RIGHT:
            bytes[3] = 0x01;
            bytes[5] = normalized_speed;
            break;
        case PTZCommand::UP_LEFT:
            bytes[3] = 0x0A;
            bytes[4] = bytes[5] = normalized_speed;
            break;
        case PTZCommand::UP_RIGHT:
            bytes[3] = 0x09;
            bytes[4] = bytes[5] = normalized_speed;
            break;
        case PTZCommand::DOWN_LEFT:
            bytes[3] = 0x06;
            bytes[4] = bytes[5] = normalized_speed;
            break;
        case PTZCommand::DOWN_RIGHT:
            bytes[3] = 0x05;
            bytes[4] = bytes[5] = normalized_speed;
            break;
        case PTZCommand::ZOOM_IN:
            bytes[3] = 0x10;
            bytes[6] = normalized_speed;
            break;
        case PTZCommand::ZOOM_OUT:
            bytes[3] = 0x20;
            bytes[6] = normalized_speed;
            break;
        case PTZCommand::PRESET_SET:
            bytes[3] = 0x81;
            bytes[7] = static_cast<uint8_t>(preset_id);
            break;
        case PTZCommand::PRESET_CALL:
            bytes[3] = 0x82;
            bytes[7] = static_cast<uint8_t>(preset_id);
            break;
        case PTZCommand::PRESET_DELETE:
            bytes[3] = 0x83;
            bytes[7] = static_cast<uint8_t>(preset_id);
            break;
        case PTZCommand::STOP:
        default:
            bytes[3] = 0x00;
            break;
    }
    
    // Convert to hex string
    for (int i = 0; i < 8; ++i) {
        oss << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(bytes[i]);
    }
    
    return oss.str();
}

int GB28181PTZ::ParsePTZSpeed(const std::string &xml)
{
    std::string speed_str = ExtractXMLValue(xml, "PTZSpeed");
    if (speed_str.empty()) {
        speed_str = ExtractXMLValue(xml, "Speed");
    }
    
    if (!speed_str.empty()) {
        try {
            int speed = std::stoi(speed_str);
            return std::max(1, std::min(255, speed)); // Clamp to valid range
        } catch (const std::exception&) {
            EL_WARN("Invalid PTZ speed value: {}", speed_str);
        }
    }
    
    return 0; // Use default
}

int GB28181PTZ::ParsePresetId(const std::string &xml)
{
    std::string preset_str = ExtractXMLValue(xml, "PresetID");
    if (preset_str.empty()) {
        preset_str = ExtractXMLValue(xml, "Preset");
    }
    
    if (!preset_str.empty()) {
        try {
            int preset = std::stoi(preset_str);
            return std::max(1, std::min(255, preset)); // Clamp to valid range
        } catch (const std::exception&) {
            EL_WARN("Invalid preset ID value: {}", preset_str);
        }
    }
    
    return 0;
}

std::string GB28181PTZ::ExtractXMLValue(const std::string &xml, const std::string &tag)
{
    std::string start_tag = "<" + tag + ">";
    std::string end_tag = "</" + tag + ">";
    
    size_t start_pos = xml.find(start_tag);
    if (start_pos == std::string::npos) {
        return "";
    }
    
    start_pos += start_tag.length();
    size_t end_pos = xml.find(end_tag, start_pos);
    if (end_pos == std::string::npos) {
        return "";
    }
    
    return xml.substr(start_pos, end_pos - start_pos);
}

bool GB28181PTZ::ValidatePTZParams(const PTZControlInfo &info)
{
    // Check device ID
    if (info.device_id.empty()) {
        EL_ERROR("Empty device ID in PTZ control");
        return false;
    }
    
    // Check command
    if (info.command == PTZCommand::UNKNOWN) {
        EL_ERROR("Unknown PTZ command");
        return false;
    }
    
    // Check speed range
    if (info.speed < 1 || info.speed > 255) {
        EL_ERROR("Invalid PTZ speed: {}", info.speed);
        return false;
    }
    
    // Check preset ID for preset operations
    if ((info.command == PTZCommand::PRESET_SET || 
         info.command == PTZCommand::PRESET_CALL || 
         info.command == PTZCommand::PRESET_DELETE) && 
        (info.preset_id < 1 || info.preset_id > 255)) {
        EL_ERROR("Invalid preset ID: {}", info.preset_id);
        return false;
    }
    
    return true;
}

} // namespace GB28181
} // namespace El